#pragma once
#include <zGraphicsConfig.hpp>
#include "Texture.hpp"
#include "../../Graphics/OpenGLTools.hpp"
#include <Image/Image.hpp>
#include <Utility/IOObject.hpp>

namespace zzz {
class ZGRAPHICS_CLASS Texture2D : public Texture
{
public:
  Texture2D(int iformat=GL_RGB);
  Texture2D(zuint width, zuint height, int iformat=GL_RGB);
  Texture2D(const string &filename, int iformat=GL_RGB);
  void Create(zuint width, zuint height);
  void Create(int internal_format);
  void Create(const string &filename);
  bool Bind(GLenum bindto=GL_TEXTURE0);
  void Unbind();
  template<typename T> bool ImageToTexture(const Image<T> &img);
  template<typename T> bool TextureToImage(Image<T> &img) const;
  template<typename T> bool FileToTexture(const string &filename);
  template<typename T> bool TextureToFile(const string &filename) const;

  void ChangeInternalFormat(int newiformat);
  void ChangeSize(zuint width,zuint height);
  void ChangeParameter(GLenum filter,GLenum wrap);
  zuint GetWidth(){return width_;}
  zuint GetHeight(){return height_;}

  static bool CopyTexture(Texture2D &from, Texture2D &to);
private:
  zuint width_, height_;
  void Create();
};

template<typename T> 
bool Texture2D::ImageToTexture(const Image<T> &img)
{
  GLvoid *data=(GLvoid *)img.Data();
  width_=img.Cols();
  height_=img.Rows();
  CHECK_GL_ERROR()
  Bind();
  CHECK_GL_ERROR()
  if (internal_format_==GL_DEPTH_COMPONENT)
    glTexImage2D(GL_TEXTURE_2D,0,internal_format_,width_,height_,0,GL_DEPTH_COMPONENT,img.Type_,data);
  else
    glTexImage2D(GL_TEXTURE_2D,0,internal_format_,width_,height_,0,img.Format_,img.Type_,data);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_para_);
  CHECK_GL_ERROR()
  return true;
}

template<typename T> 
bool Texture2D::TextureToImage(Image<T> &img) const 
{
  CHECK_GL_ERROR()
  if (!IsValid()) {
    ZLOGE<<"Calling TextureToImage on a invalid texture!\n";
    return false;
  }
  CHECK_GL_ERROR()
  glBindTexture(GL_TEXTURE_2D,tex_);
  CHECK_GL_ERROR()
  img.SetSize(height_,width_);
  GLvoid *data=(GLvoid *)img.Data();
  if (internal_format_==GL_DEPTH_COMPONENT && img.Format_==ZZZ_LUMINANCE)
    glGetTexImage(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,img.Type_, data);
  else
    glGetTexImage(GL_TEXTURE_2D,0,img.Format_,img.Type_,data);
  CHECK_GL_ERROR()
  return true;
}

template<typename T> 
bool Texture2D::FileToTexture(const string &filename)
{
  Image<T> img(filename);
  return ImageToTexture(img);
}

template<typename T> 
bool Texture2D::TextureToFile(const string &filename) const
{
  Image<T> img;
  return TextureToImage(img) && img.SaveFile(filename);
}

}
